/*
 * Famon (http://code.google.com/p/famon/)
 * Copyright (c) 2010 Carl Crous.
 *
 * This file is part of Famon.
 *
 * Famon is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Famon is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with Famon.  If not, see <http://www.gnu.org/licenses/>.
 */

package za.org.foobar.famon.frontend.client.framework;

import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.user.client.EventListener;
import za.org.foobar.famon.frontend.client.BaseCallback;
import za.org.foobar.famon.frontend.client.FamonService;
import za.org.foobar.famon.frontend.client.dto.CModelObjectSuggestion;

public class ModelObjectSuggestBox extends Composite implements EventListener,
        HasText, HasValueChangeHandlers<String>, HasSelectionHandlers<SuggestOracle.Suggestion> {

    private String selectedId;
    private String selectedValue;
    private SuggestBox lookup;
    private final String dbClassName;
    private final String dbValueField;
    private final String dbWhere;
    private CModelObjectSuggestion[] suggestions;

    public ModelObjectSuggestBox(final TextBoxBase box, final String dbClassName,
                                 final String dbValueField, final String dbWhere) {
        lookup = new SuggestBox(new ModelObjectSuggestOracle(), box);
        initWidget(lookup);
        this.dbClassName = dbClassName;
        this.dbValueField = dbValueField;
        this.dbWhere = dbWhere;

        lookup.addSelectionHandler(new SelectionHandler<SuggestOracle.Suggestion>() {
            public void onSelection(SelectionEvent<SuggestOracle.Suggestion> event) {
                selectedValue = event.getSelectedItem().getReplacementString();
                selectedId = getIdForValue(selectedValue);
                SelectionEvent.fire(ModelObjectSuggestBox.this, event.getSelectedItem());
            }
        });
    }

    public HandlerRegistration addValueChangeHandler(ValueChangeHandler<String> handler) {
        return addHandler(handler, ValueChangeEvent.getType());
    }

    public HandlerRegistration addSelectionHandler(SelectionHandler<SuggestOracle.Suggestion> handler) {
        return addHandler(handler, SelectionEvent.getType());
    }

    public String getText() {
        return selectedValue;
    }

    public void setText(String s) {
        selectedValue = s;
        lookup.setText(s);
    }

    public String getSelectedId() {
        return selectedId;
    }

    public void setSelectedId(String selectedId) {
        this.selectedId = selectedId;
    }

    private void updateSuggestions(CModelObjectSuggestion[] suggestions) {
        this.suggestions = suggestions;
        MultiWordSuggestOracle so = ((ModelObjectSuggestOracle)lookup.getSuggestOracle()).multiOracle;
        so.clear();
        if (suggestions != null) {
            for (CModelObjectSuggestion c : suggestions) {
                if (c != null) {
                    so.add(c.value);
                }
            }
        }
    }

    private String getIdForValue(String value) {
        if (suggestions != null) {
            for (CModelObjectSuggestion c : suggestions) {
                if (c.value.equals(value)) {
                    return c.id;
                }
            }
        }
        return null;
    }

    public class ModelObjectSuggestOracle extends SuggestOracle {
        public MultiWordSuggestOracle multiOracle = new MultiWordSuggestOracle();
        private String lastPrefix;
        private boolean useLastResult;

        @Override
        public void requestSuggestions(Request request, Callback callback) {
            String query = request.getQuery().toLowerCase();
            if (lastPrefix != null && query.startsWith(lastPrefix) &&
                    (lastPrefix.trim().equals(query.trim()) || useLastResult)) {
                multiOracle.requestSuggestions(request, callback);
                return;
            }
            lastPrefix = query;
            FamonService.App.get().getModelObjects(dbClassName, dbValueField, dbWhere,
                    request.getQuery(), new SuggestionCallback(request, callback));
        }

        public class SuggestionCallback extends BaseCallback<CModelObjectSuggestion[]> {
            private Request request;
            private Callback callback;

            public SuggestionCallback(Request request, Callback callback) {
                this.request = request;
                this.callback = callback;
            }

            public void onSuccess(CModelObjectSuggestion[] contacts) {
                useLastResult = contacts.length > 0 && contacts[contacts.length-1] != null;
                updateSuggestions(contacts);
                multiOracle.requestSuggestions(request, callback);
            }

            @Override
            public void onFailure(Throwable caught) {
                lastPrefix = null;
            }
        }

        @Override
        public boolean isDisplayStringHTML() {
            return true;
        }
    }

}
